﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using DevExpress.XtraGrid.Views.Grid;
using ProtocolVN.Framework.Core;
using System.Drawing;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid;
using System.Data.Common;

namespace ProtocolVN.Framework.Win
{
    /// <summary>
    /// DUYVT
    /// Ràng buộc này sử dụng cho các loại phiếu mà 1 hàng hóa phân biệt thông qua ID và tình trạng
    /// Hiện tại được sử dụng trong trường hợp: 
    /// Phiếu kiểm kho tạo Phiếu xử lý kiểm kho,
    /// Phiếu xử lý kiểm kho tạo Phiếu xuất/nhập kho,
    /// Phiếu chuyển kho(nhiều loại) tạo Phiếu chuyển kho(nhiều loại)
    /// Phiếu nhập kho chờ nhập tạo Phiếu chuyển kho Chờ nhập/Nhập thực
    /// </summary>
    public class RangBuocSoLgTrLgExt : RangBuocRule
    {
        #region RangBuocRule Members
        DataTable ConLai;
        PhieuType Src;
        long SrcID;
        PhieuType Des;
        long DesID;
        object DesObj;
        PLGridView DesGrid;

        public RangBuocSoLgTrLgExt() { }

        public RangBuocSoLgTrLgExt(PhieuType Src, long SrcID, PhieuType Des, long DesID, 
            object DesObj, PLGridView DesGrid)
        {
            this.Src = Src;
            this.SrcID = SrcID;
            this.Des = Des;
            this.DesID = DesID;
            this.DesObj = DesObj;
            this.DesGrid = DesGrid;
        }

        //Trả về số lượng, trọng lượng còn lại có thể chấp nhận
        private bool Check(PhieuType Src, long SrcID, List<object[]> LObj, PhieuType Des, 
            long DesID, object DesObj, PLGridView DesGrid)
        {
            ISoLgTrLg data = (ISoLgTrLg)DesObj;
            //Tạo DataTable(HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG)
            DataTable dt_lienquan = RangBuocSoLgTrLgExtHelp.CreateDTHangHoa(LObj, DesID);
            
            DataTable dt_sum = new DataTable();            
            dt_sum = RangBuocSoLgTrLgExtHelp.GetData_Standard(Src, SrcID).Tables[0];
                    
            //Lấy HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG cua DesObj nếu tồn tại
            if (data != null)
            {
                DataTable dt_des = data.GetSoLgTrLg().Copy();
                if (RangBuocSoLgTrLgExtHelp.DieuChinhTenCot(dt_des, Des))
                {
                    //Merge thêm vào dt_lienquan từ dt_des
                    RangBuocSoLgTrLgExtHelp.MergeAddHangHoaExt(ref dt_lienquan, dt_des);
                }
            }     
            //Kiểm tra ràng buộc về số lượng và trọng lượng
            //Kết quả trả về DataTable miêu tả các dòng vi phạm
            DataTable dt_vipham = RangBuocSoLgTrLgExtHelp.KiemTraRangBuocExt(dt_sum, dt_lienquan);
            if (dt_vipham.Rows.Count != 0 && DesGrid != null)            
                RangBuocSoLgTrLgExtHelp.ShowErrorOnGridExt(DesGrid, dt_vipham, Des);            

            //Merge giảm vào dt_sum từ dt_lienquan
            RangBuocSoLgTrLgExtHelp.MergeMinusHangHoaExt(ref dt_sum, dt_lienquan);
            //Set số lượng còn lại
            ConLai = dt_sum;

            //Nếu dt_vipham không tồn tại dòng vi phạm và phiếu nguồn chưa tạo hết phiếu liên quan
            bool flag = false;            
            if (data != null)
            {
                if (dt_vipham.Rows.Count == 0)
                    flag = true;
            }
            else 
            {
                if (RangBuocSoLgTrLgHelp.KiemTraTaoPhieu(ConLai))
                    flag = true;
            }
            return flag;
        }
        
        public DataTable GetConLai(PhieuType Des)
        {
            foreach (DataRow dr in ConLai.Rows)
            {
                if (HelpNumber.ParseDecimal(dr["SO_LUONG"]) <= 0 ||
                    HelpNumber.ParseDecimal(dr["TRONG_LUONG"]) <= 0)
                {
                    dr.Delete();
                }
            }
            if(RangBuocSoLgTrLgHelp.CheckColumnsExist(ConLai, "TINH_TRANG"))
                ConLai.Columns["TINH_TRANG"].ColumnName = Des.GetSTTField();
            return ConLai;
        }
        #endregion

        #region RangBuocRule Members

        public List<InvalidInfo> Check()
        {
            List<InvalidInfo> ret = new List<InvalidInfo>();
            List<object[]> LObj = DAPhieuLienQuan.GetListPhieuLienQuan(SrcID, Src, Des);
            bool flag = Check(Src, SrcID, LObj, Des, DesID, DesObj, DesGrid);
            if (flag == false)
            {
                ret.Add(new InvalidInfo(
                    "Vi phạm ràng buộc số lượng, trọng lượng", 
                    "Vui lòng kiểm tra lại các thông tin liên quan số lượng và trọng lượng", 
                    InvalidInfo.InvalidType.ERROR));
            }
            return ret;
        }

        #endregion

        public static bool Check(RangBuocSoLgTrLgExt rule, PhieuType Src, long SrcID, 
            PhieuType Des, long DesID, Object DesObj, PLGridView DesGrid)
        {
            //Lay danh sach cac phieu thuoc nhom phieu des duoc tao tu (Src, SrcID)
            //Duoc xay dung tu PhieuLienQuan List (TYPE_ID, ID)            
            List<object[]> LObj = DAPhieuLienQuan.GetListPhieuLienQuan(SrcID, Src, Des);
            return rule.Check(Src, SrcID, LObj, Des, DesID, DesObj, DesGrid);
        }
    }

    public class RangBuocSoLgTrLgExtHelp
    {        
        /// <summary>
        /// Tạo DataTable(HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG) lưu trữ hàng hóa, 
        /// kèm số lượng, trọng lượng và tình trạng
        /// (TINH_TRANG dùng trong trường hợp cùng 1 hàng hóa nhưng có nhiều tình trạng khác nhau)
        /// Sử dụng trong việc kiểm tra ràng buộc về số lượng và trọng lượng
        /// </summary>
        /// <param name="LObj">Danh sách phiếu liên quan (Loại phiếu, ID)</param>
        /// <param name="Des">Loại phiếu đích</param>
        /// <param name="DesID">ID phiếu đích</param>        
        /// <returns>DataTable(HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG)</returns>
        public static DataTable CreateDTHangHoa(List<object[]> LObj, long DesID)
        {
            DataTable dt_rough = new DataTable();
            DataColumn[] dc_arr = new DataColumn[] { new DataColumn("HH_ID"),
                new DataColumn("SO_LUONG"),
                new DataColumn("TRONG_LUONG"),
                new DataColumn("TINH_TRANG")};
            dt_rough.Columns.AddRange(dc_arr);

            dt_rough.Columns["SO_LUONG"].DataType = Type.GetType("System.Decimal");
            dt_rough.Columns["TRONG_LUONG"].DataType = Type.GetType("System.Decimal");

            try
            {
                foreach (object[] obj_arr in LObj)
                {
                    PhieuType PLQ_type = (PhieuType)obj_arr[0];
                    long PLQ_id = (long)obj_arr[1];
                    if (DesID == -1 || (DesID != -1 && DesID != PLQ_id))
                    {
                        QueryBuilder filter = new QueryBuilder("select hh_id as \"HH_ID\", "
                            + PLQ_type.GetSOLGField() + 
                            " as \"SO_LUONG\"," + PLQ_type.GetTRLGField() + 
                            " as \"TRONG_LUONG\"," + PLQ_type.GetSTTField() +
                            " as \"TINH_TRANG\" from " + PLQ_type.GetTableName()
                            + "_CT" + " where 1=1");
                        filter.addID(PLQ_type.GetIDField(), PLQ_id);
                        DataSet ds = DABase.getDatabase().LoadDataSet(filter);
                        DataRow[] dr_arr = ds.Tables[0].Select();
                        foreach (DataRow dr in dr_arr)
                            dt_rough.ImportRow(dr);
                    }
                }

                DataTable dt_last = dt_rough.Clone();
                DataTable dt_distinct = RangBuocSoLgTrLgHelp.SelectDistinct(dt_rough, "HH_ID", "TINH_TRANG");
                foreach (DataRow dr in dt_distinct.Rows)
                {
                    DataRow _dr = dt_last.NewRow();
                    _dr["HH_ID"] = dr["HH_ID"];
                    _dr["SO_LUONG"] = dt_rough.Compute("Sum(SO_LUONG)", "HH_ID='" + dr["HH_ID"] + 
                        "' and TINH_TRANG='" + dr["TINH_TRANG"] + "'");
                    _dr["TRONG_LUONG"] = dt_rough.Compute("Sum(TRONG_LUONG)", "HH_ID='" + dr["HH_ID"] + 
                        "' and TINH_TRANG='" + dr["TINH_TRANG"] + "'");
                    _dr["TINH_TRANG"] = dr["TINH_TRANG"];
                    dt_last.Rows.Add(_dr);
                }
                return dt_last;
            }
            catch (Exception ex)
            {
                PLException.AddException(ex);
                return null;
            }
        }        

        /// <summary>
        /// Merge thêm số lượng, trọng lượng vào dt_main từ dt_add 
        /// </summary>
        /// <param name="dt_main">DataTable cần merge</param>
        /// <param name="dt_add">DataTable thêm</param>            
        public static void MergeAddHangHoaExt(ref DataTable dt_main, DataTable dt_add)
        {

            if (!RangBuocSoLgTrLgHelp.CheckColumnsExist(
                    dt_main, "HH_ID", "SO_LUONG", "TRONG_LUONG", "TINH_TRANG") ||
                !RangBuocSoLgTrLgHelp.CheckColumnsExist(
                    dt_add, "HH_ID", "SO_LUONG", "TRONG_LUONG", "TINH_TRANG"))
            {
                return;
            }

            dt_main.Columns.Add("SO_LUONG_DICH_DA_CO", Type.GetType("System.Decimal"));
            dt_main.Columns.Add("TRONG_LUONG_DICH_DA_CO", Type.GetType("System.Decimal"));
            foreach (DataRow dr in dt_add.Rows)
            {
                if (dr.RowState != DataRowState.Deleted)
                {
                    int i = CheckHangHoaExistExt(dt_main, HelpNumber.ParseInt64(dr["HH_ID"]), 
                        HelpNumber.ParseInt64(dr["TINH_TRANG"]));
                    if (i != -1)
                    {
                        dt_main.Rows[i]["SO_LUONG_DICH_DA_CO"] = dt_main.Rows[i]["SO_LUONG"];
                        dt_main.Rows[i]["TRONG_LUONG_DICH_DA_CO"] = dt_main.Rows[i]["TRONG_LUONG"];
                        dt_main.Rows[i]["SO_LUONG"] = HelpNumber.ParseDecimal(dt_main.Rows[i]["SO_LUONG"])
                            + HelpNumber.ParseDecimal(dr["SO_LUONG"]);
                        dt_main.Rows[i]["TRONG_LUONG"] = HelpNumber.ParseDecimal(dt_main.Rows[i]["TRONG_LUONG"])
                            + HelpNumber.ParseDecimal(dr["TRONG_LUONG"]);
                    }
                    else
                    {
                        DataRow _dr = dt_main.NewRow();
                        _dr["HH_ID"] = dr["HH_ID"];
                        _dr["SO_LUONG"] = dr["SO_LUONG"];
                        _dr["TRONG_LUONG"] = dr["TRONG_LUONG"];
                        _dr["TINH_TRANG"] = dr["TINH_TRANG"];
                        dt_main.Rows.Add(_dr);
                    }
                }
            }
        }

        /// <summary>
        /// Merge giảm số lượng, trọng lượng vào dt_main từ dt_minus
        /// </summary>
        /// <param name="dt_main">DataTable cần merge</param>
        /// <param name="dt_add">DataTable giảm</param>
        public static void MergeMinusHangHoaExt(ref DataTable dt_main, DataTable dt_minus)
        {
            if (!RangBuocSoLgTrLgHelp.CheckColumnsExist(
                    dt_main, "HH_ID", "SO_LUONG", "TRONG_LUONG", "TINH_TRANG") ||
                !RangBuocSoLgTrLgHelp.CheckColumnsExist(
                    dt_minus, "HH_ID", "SO_LUONG", "TRONG_LUONG", "TINH_TRANG"))
            {
                return;
            }

            foreach (DataRow dr in dt_minus.Rows)
            {
                int i = CheckHangHoaExistExt(dt_main, HelpNumber.ParseInt64(dr["HH_ID"]), 
                    HelpNumber.ParseInt64(dr["TINH_TRANG"]));
                if (i != -1)
                {
                    dt_main.Rows[i]["SO_LUONG"] = HelpNumber.ParseDecimal(dt_main.Rows[i]["SO_LUONG"])
                        - HelpNumber.ParseDecimal(dr["SO_LUONG"]);
                    dt_main.Rows[i]["TRONG_LUONG"] = HelpNumber.ParseDecimal(dt_main.Rows[i]["TRONG_LUONG"])
                        - HelpNumber.ParseDecimal(dr["TRONG_LUONG"]);
                }
            }
        }

        /// <summary>
        /// Kiểm tra ràng buộc về số lượng, trọng lượng
        /// </summary>
        /// <param name="dt_nguon">DataTable nguồn</param>
        /// <param name="dt_dich">DataTable đích</param>
        /// <returns>DataTable miêu tả các dòng không hợp lệ</returns>
        public static DataTable KiemTraRangBuocExt(DataTable dt_nguon, DataTable dt_dich)
        {
            DataTable dtViPham = new DataTable();            
            dtViPham.Columns.Add("HH_ID", Type.GetType("System.Int64"));
            dtViPham.Columns.Add("TINH_TRANG");
            dtViPham.Columns.Add("VI_PHAM");
            
            if (dt_nguon == null || dt_nguon.Rows.Count == 0 || dt_dich == null)
                return dtViPham;

            foreach (DataRow dr_dich in dt_dich.Rows)
            {
                bool exists = false;
                DataRow rowViPham = dtViPham.NewRow();
                foreach (DataRow dr_nguon in dt_nguon.Rows)
                {
                    if ((HelpNumber.ParseInt64(dr_dich["HH_ID"]) == HelpNumber.ParseInt64(dr_nguon["HH_ID"]))
                        && (HelpNumber.ParseInt64(dr_dich["TINH_TRANG"]) == HelpNumber.ParseInt64(dr_nguon["TINH_TRANG"])))
                    {
                        //HH_ID,TINH_TRANG có tồn tại trong phiếu nguồn
                        exists = true;                        
                        decimal soLuongDich = HelpNumber.ParseDecimal(dr_dich["SO_LUONG"]);
                        decimal soLuongNguon = HelpNumber.ParseDecimal(dr_nguon["SO_LUONG"]);
                        decimal trongLuongDich = HelpNumber.ParseDecimal(dr_dich["TRONG_LUONG"]);
                        decimal trongLuongNguon = HelpNumber.ParseDecimal(dr_nguon["TRONG_LUONG"]);
                        decimal soLuongDichDaCo = 0;
                        decimal trongLuongDichDaCo = 0;

                        if (dt_dich.Columns.Contains("SO_LUONG_DICH_DA_CO") &&
                            dt_dich.Columns.Contains("TRONG_LUONG_DICH_DA_CO"))
                        {
                            soLuongDichDaCo = HelpNumber.ParseDecimal(dr_dich["SO_LUONG_DICH_DA_CO"]);
                            trongLuongDichDaCo = HelpNumber.ParseDecimal(dr_dich["TRONG_LUONG_DICH_DA_CO"]);
                        }
                        if ((soLuongDich > soLuongNguon) || (trongLuongDich > trongLuongNguon))
                        {
                            string viPham = "";
                            rowViPham["HH_ID"] = dr_dich["HH_ID"];
                            rowViPham["TINH_TRANG"] = dr_dich["TINH_TRANG"];
                            if (soLuongDich > soLuongNguon)
                            {
                                viPham = "Vi phạm ràng buộc về số lượng. Số lượng cho phép: <=" + 
                                    (soLuongNguon - soLuongDichDaCo) + " !";
                            }
                            if (trongLuongDich > trongLuongNguon)
                            {
                                viPham = "Vi phạm ràng buộc về trọng lượng. Trọng lượng cho phép: <=" + 
                                    (trongLuongNguon - trongLuongDichDaCo) + " !";
                            }
                            if ((soLuongDich > soLuongNguon) && (trongLuongDich > trongLuongNguon))
                            {
                                viPham = "Vi phạm ràng buộc về số lượng và trọng lượng. SLg cho phép:<=" + 
                                    (soLuongNguon - soLuongDichDaCo) + "; TrLg cho phép: <=" + 
                                    (trongLuongNguon - trongLuongDichDaCo) + " !";
                            }
                            rowViPham["VI_PHAM"] = viPham;
                            if (CheckHangHoaExistExt(dtViPham, HelpNumber.ParseInt64(rowViPham["HH_ID"]),
                                HelpNumber.ParseInt64(rowViPham["TINH_TRANG"])) == -1)
                            {
                                dtViPham.Rows.Add(rowViPham);
                            }
                        }
                    }
                }
                if (!exists)
                {
                    rowViPham["HH_ID"] = dr_dich["HH_ID"];
                    rowViPham["TINH_TRANG"] = dr_dich["TINH_TRANG"];
                    rowViPham["VI_PHAM"] = "Hàng hóa này không có trong phiếu nguồn";                    
                    dtViPham.Rows.Add(rowViPham);
                }
            }
            return dtViPham;
        }

        /// <summary>
        /// Thể hiện lỗi trên Grid dựa vào DataTable chứa các dòng lỗi        
        /// </summary>
        /// <param name="gridDich">Grid cần thể hiện lỗi</param>
        /// <param name="dt_error">DataTable các dòng lỗi</param>
        public static void ShowErrorOnGridExt(PLGridView gridDich, DataTable dt_error, PhieuType Des)
        {
            RangBuocSoLgTrLgHelp.AddFormatCond(gridDich);            
            for (int i = 0; i < gridDich.RowCount; i++)
            {
                gridDich.SetRowCellValue(i, gridDich.Columns["CHECK"], 1);
                gridDich.SetRowCellValue(i, gridDich.Columns["VI_PHAM"], "");
                foreach (DataRow dr_error in dt_error.Rows)
                {
                    DataRow rowDich = gridDich.GetDataRow(i);
                    if ((HelpNumber.ParseInt64(rowDich["HH_ID"]) == HelpNumber.ParseInt64(dr_error["HH_ID"]))
                        && (HelpNumber.ParseInt64(rowDich[Des.GetSTTField()]) == HelpNumber.ParseInt64(dr_error["TINH_TRANG"])))
                    {
                        gridDich.SetRowCellValue(i, gridDich.Columns["CHECK"], 0);
                        gridDich.SetRowCellValue(i, gridDich.Columns["VI_PHAM"], dr_error["VI_PHAM"]);
                        gridDich.UpdateCurrentRow();
                        gridDich.OptionsView.ShowPreview = true;
                        gridDich.OptionsView.AutoCalcPreviewLineCount = true;                        
                        gridDich.PreviewFieldName = "VI_PHAM";
                        gridDich.CustomDrawRowPreview += 
                            new DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventHandler(
                                RangBuocSoLgTrLgHelp.gridDich_CustomDrawRowPreview);                                                
                    }
                }
            }
        }        

        /// <summary>
        /// Hàm hổ trợ cho MergeAddHangHoa
        /// </summary>
        /// <param name="dt">DataTable</param>
        /// <param name="id">ID hàng hóa</param>
        /// <returns>Vị trí hàng hóa trong DataTable</returns>
        public static int CheckHangHoaExistExt(DataTable dt, long hh_id, long tinh_trang)
        {
            if (dt == null)
                return -1;

            try
            {
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    DataRow dr = dt.Rows[i];
                    if (HelpNumber.ParseInt64(dr["HH_ID"]).Equals(hh_id)
                        && HelpNumber.ParseInt64(dr["TINH_TRANG"]).Equals(tinh_trang))
                    {
                        return i;
                    }
                }
                return -1;
            }
            catch (Exception ex)
            {
                PLException.AddException(ex);
                return -1;
            }
        }

        /// <summary>
        /// Tạo DataTable(HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG) lưu trữ hàng hóa, 
        /// số lượng, trọng lượng và tình trạng
        /// Thuộc 1 phiếu bất kì có loại phiếu và ID
        /// </summary>
        /// <param name="type">Loại phiếu</param>
        /// <param name="id">ID</param>
        /// <returns>DataSet chứa DataTable(HH_ID, SO_LUONG, TRONG_LUONG, TINH_TRANG)</returns>
        public static DataSet GetData_Standard(PhieuType type, long id)
        {
            try
            {
                QueryBuilder filter = new QueryBuilder("select hh_id as \"HH_ID\","
                    + type.GetSOLGField() + " as \"SO_LUONG\","
                    + type.GetTRLGField() + " as \"TRONG_LUONG\","
                    + type.GetSTTField() + " as \"TINH_TRANG\" from "
                    + type.GetTableName() + "_CT" + " where 1=1");
                filter.addID(type.GetIDField(), id);
                return DABase.getDatabase().LoadDataSet(filter);                
            }
            catch (Exception ex)
            {
                PLException.AddException(ex);
                return null;
            }
        }        

        /// <summary>
        /// Thêm trọng lượng hàng hóa vào Grid
        /// </summary>
        /// <param name="GridDataSet">Grid cần thêm trọng lượng</param>
        public static void SetTrongLuong(DataSet GridDataSet)        
        {            
            GridDataSet.Tables[0].Columns.Add("TRONG_LUONG");
            foreach (DataRow row in GridDataSet.Tables[0].Rows)
            {
                DatabaseFB db = DABase.getDatabase();
                DataSet ds = new DataSet();
                string stsrql = "SELECT TRONG_LUONG_HANG FROM DM_HANG_HOA WHERE ID=" + 
                    row["HH_ID"].ToString() + "";
                DbCommand cmd = db.GetSQLStringCommand(stsrql);
                db.LoadDataSet(cmd, ds, "TRONG_LUONG");
                if (ds.Tables[0].Rows.Count != 0)
                {
                    row["TRONG_LUONG"] = HelpNumber.ParseDecimal(ds.Tables[0].Rows[0][0]) * 
                        HelpNumber.ParseDecimal(row["SO_LUONG"]);
                }
            }
        }

        /// <summary>
        /// Cập nhật lại tên cột tình trạng cho phù hợp với mục đích sử dụng 
        /// </summary>
        /// <param name="table">DataTable cần cập nhật lại tên cột tình trạng</param>
        /// <param name="Des">Loại phiếu đích</param>
        /// <returns></returns>
        public static bool DieuChinhTenCot(DataTable table, PhieuType Des)
        {
            if (!RangBuocSoLgTrLgHelp.CheckColumnsExist(table, Des.GetSTTField(),
                Des.GetSOLGField(), Des.GetTRLGField()))
            {
                return false;
            }

            foreach (DataColumn dc in table.Columns)
            {
                if (dc.ColumnName == Des.GetSTTField())
                    dc.ColumnName = "TINH_TRANG";
                else if (dc.ColumnName == Des.GetSOLGField())
                    dc.ColumnName = "SO_LUONG";
                else if (dc.ColumnName == Des.GetTRLGField())
                    dc.ColumnName = "TRONG_LUONG";                
            }
            return true;
        }
    }
}
